package com.cgs.query.remote.http

import com.cgs.query.exception.QueryException
import com.cgs.query.handler.HELLO_METHOD
import com.cgs.query.handler.RemoteHandler
import com.cgs.query.handler.Request
import com.cgs.query.handler.Response
import com.cgs.query.util.GsonUtils
import com.google.gson.*
import mu.KotlinLogging
import org.springframework.http.HttpEntity
import org.springframework.http.HttpHeaders
import org.springframework.http.MediaType
import org.springframework.web.client.RestClientException
import org.springframework.web.client.RestTemplate
import org.springframework.web.context.request.RequestContextHolder
import org.springframework.web.context.request.ServletRequestAttributes
import java.lang.reflect.Type
import java.text.ParseException
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.atomic.AtomicInteger
import kotlin.collections.ArrayList

private val logger = KotlinLogging.logger { }

const val ARGS = "args"

const val METHOD_NAME = "method_name"

class HttpRemoteHandler(
        private val restTemplate: RestTemplate,
        private val urls: Array<String>) : RemoteHandler {

    private val completeUrls = urls.map { "$it/remote" }.toCollection(ArrayList())

    private val index = AtomicInteger(1)

    override fun hello(appName: String) {
        transport(Request(-1L, HELLO_METHOD, listOf(appName), String::class.java))
    }

    override fun transport(request: Request): Response {
        val map = HashMap<String, String>()
        map[ARGS] = GsonUtils.gson.toJson(request.args)
        map[METHOD_NAME] = request.methodName
        while (completeUrls.isNotEmpty()) {
            val url = route()
            try {
                return Response(request.requestId, send(url, map, request.returnType))
            } catch (e: Exception) {
                completeUrls.remove(url)
                logger.error(e) { "连接地址无效: $url , 正在尝试其他选择" }
            }
        }
        throw QueryException("所有连接地址无效：${Arrays.toString(urls)} ")
    }

    override fun urlTransport(url: String, request: Request): Response {
        val map = HashMap<String, String>()
        map[ARGS] = GsonUtils.gson.toJson(request.args)
        map[METHOD_NAME] = request.methodName
        try {
            return Response(request.requestId, send(url, map, request.returnType))
        } catch (e: Exception) {
            throw QueryException("连接地址无效: $url", e)
        }
    }

    override fun getRemoteType() = "http"

    /**
     * 发送请求
     */
    private fun <T> send(url: String, param: Map<String, String>, returnType: Class<T>): T? {
        try {
            val result = restTemplate.postForEntity(url,
                    HttpEntity(param, Holder.headers.apply {
                        this.set("from", (RequestContextHolder.getRequestAttributes() as? ServletRequestAttributes)?.request?.requestURL.toString())
                    })
                    , String::class.java)
            if (!result.statusCode.is2xxSuccessful) {
                throw QueryException("远程调用错误:${result.statusCode} ")
            }
            return GsonUtils.gson.fromJson(result.body, returnType)
        } catch (e: RestClientException) {
            throw QueryException("请确保服端开启:$url ", e)
        }
    }

    /**
     * 负载均衡
     */
    private fun route(): String {
        return completeUrls.route()
    }

    private fun ArrayList<String>.route(): String {
        return this[index.getAndIncrement() % this.size]
    }
}


object Holder {

    val headers = HttpHeaders()

    init {
        headers.contentType = MediaType.APPLICATION_JSON
    }
}